package com.hgx.su.common.springboot.web.aop;

import com.hgx.su.common.pure.dto.RestLogDto;
import com.hgx.su.common.springboot.web.util.IpUtil;
import com.hgx.su.common.third.json.JacksonUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * Description: rest日志切面
 * author: HGX
 * DateTime: 2020-12-21 19:12
 */
@Aspect
@Component
public class RestLogAspect {


    private static Logger log = LoggerFactory.getLogger(RestLogAspect.class);

    /**
     * Controller aspect.
     */
    @Pointcut(
            "@annotation(org.springframework.web.bind.annotation.RequestMapping) || " +
                    "@annotation(org.springframework.web.bind.annotation.GetMapping)||" +
                    "@annotation(org.springframework.web.bind.annotation.PutMapping)||" +
                    "@annotation(org.springframework.web.bind.annotation.DeleteMapping)||" +
                    "@annotation(org.springframework.web.bind.annotation.PostMapping)")
    public void RestAspect() {
    }

    /**
     * Around 手动控制调用核心业务逻辑，以及调用前和调用后的处理,
     * <p>
     * 注意：当核心业务抛异常后，立即退出，转向AfterAdvice 执行完AfterAdvice，再转到ThrowingAdvice
     *
     * @param pjp the pjp
     * @return object
     * @throws Throwable the throwable
     */
    @Around(value = "RestAspect()")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        //防止不是http请求的方法，例如：scheduled
        if (ra == null) {
            return pjp.proceed();
        }
        HttpServletRequest request = sra.getRequest();

        //HeartBeatController不打印日志
        String declaringTypeName = pjp.getSignature().getDeclaringTypeName();
        String className = declaringTypeName.substring(declaringTypeName.lastIndexOf(".") + 1);
        if ("ServiceHealthyController".equals(className)) {
            return pjp.proceed();
        }


        RestLogDto restLogDto = new RestLogDto();

        restLogDto.setRequestUrl(request.getRequestURL().toString());
        restLogDto.setHttpMethod(request.getMethod());
        restLogDto.setIp(IpUtil.getIpAddr(request));
        restLogDto.setClassMethod(pjp.getSignature().getDeclaringTypeName() + "." + pjp.getSignature().getName());

        //对于包装的了才反序列打印
        if (pjp.getArgs() == null || pjp.getArgs().length <= 0) {
            restLogDto.setRequestArgs("");
        } else {
            restLogDto.setRequestArgs(pjp.getArgs());
        }

        long startTime = System.currentTimeMillis();
        try {
            Object response = pjp.proceed();
            // 3.出参打印
            restLogDto.setResponseArgs(response != null ? response : "");
            return response;
        } catch (Throwable e) {
            restLogDto.setExceptionStr(e.getMessage() + ":" + e.getStackTrace()[0]);
            throw e;
        } finally {
            long endTime = System.currentTimeMillis();
            restLogDto.setConsumingTime((endTime - startTime) + "ms");
            log.info("restLog-->:{}", JacksonUtil.objToString(restLogDto));
        }
    }

}
